package com.ppch.wuwamanus.tools;

import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ppch.wuwamanus.model.vo.draw_card_analysis.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;

import java.io.PrintStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.util.*;
import java.util.function.Supplier;

/**
 * Package:WuWaManus
 * ClassName:CardDrawingAnalysisTool
 *
 * @Author 泡泡茶壶
 * @Create 2025/8/13 16:49
 * @Version 1.0
 * Description:
 * 《鸣潮》抽卡分析（只对前四类卡池进行分析：角色活动、武器活动、角色常驻、武器常驻 四类）
 */
@Slf4j
public class CardDrawingAnalysisTool {

    /**
     * 用户 UID
     */
    private String UID;

    /**
     * 总抽卡数
     */
    private Integer drawCardTotalCount = 0;

    /**
     * 总金数
     */
    private Integer goldenLegendTotalCount = 0;

    /**
     * 已出金抽卡数
     */
    private Double goldenLegendDrawCardCount = 0.0;

    /**
     * 平均出金数
     */
    private Double goldenLegendAvgCount = 0.0;

    /**
     * 获取用户抽卡分析结果
     *
     * @return 用户抽卡分析结果
     */
    @Tool(description = "根据用户提供的 URL 获取用户的抽卡分析结果，获取结果后请调用 doTerminate 工具结束任务。")
    public String getCardDrawingAnalysisResult(@ToolParam(description = "《鸣潮》抽卡分析结果页面 URL") String url) {
        try {
            // 强制设置标准输出为UTF-8
            System.setOut(new PrintStream(System.out, true, StandardCharsets.UTF_8));
            //1.获取用户不同卡池的抽卡结果列表（4种卡池）
            Map<Integer, List<Map<String, Object>>> cardDrawingDataToList = getCardDrawingDataToList(url);
            //2.对用户的抽卡结果进行分析
            return analysisCardDrawingData(cardDrawingDataToList);
        } catch (Exception e) {
            return "链接已过期或是非法链接。";
        }
    }


    /**
     * 6.分析抽卡结果列表，返回分析结果
     * 1.UID
     * 2.总抽卡数（4个类型卡池抽数相加）
     * 3.总金数（4个类型卡池金数相加）
     * 4.平均出金数（4个类型已出金抽卡数/4个卡池总金数）
     * 5.角色活动池：总抽卡数，出金平均抽数（UP角色），出金数（cardPoolType：1）
     * 6.武器活动池：总抽卡数，出金平均抽数，出金数（cardPoolType：2）
     * 7.常驻角色池：总抽卡数，出金平均抽数，出金数（cardPoolType：3）
     * 8.常驻武器池：总抽卡数，出金平均抽数，出金数（cardPoolType：4）
     *
     * @param cardPoolTypeCardDrawingResultMap 不同卡池对应的抽卡结果列表
     */
    public String analysisCardDrawingData(Map<Integer, List<Map<String, Object>>> cardPoolTypeCardDrawingResultMap) {
        CharacterActivityPoolVO characterActivityPoolVO = getPoolParameters(cardPoolTypeCardDrawingResultMap.get(1),
                CharacterActivityPoolVO::new, "角色活动池");
        WeaponActivityPoolVO weaponActivityPoolVO = getPoolParameters(cardPoolTypeCardDrawingResultMap.get(2),
                WeaponActivityPoolVO::new, "武器活动池");
        CharacterResidentPoolVO characterResidentPoolVO = getPoolParameters(cardPoolTypeCardDrawingResultMap.get(3),
                CharacterResidentPoolVO::new, "角色常驻池");
        WeaponResidentPoolVO weaponResidentPoolVO = getPoolParameters(cardPoolTypeCardDrawingResultMap.get(4),
                WeaponResidentPoolVO::new, "武器常驻池");
        //2.总抽卡数（每个卡池总抽卡数相加）
        drawCardTotalCount = characterActivityPoolVO.getTotalPulls()+weaponActivityPoolVO.getTotalPulls()
                +characterResidentPoolVO.getTotalPulls()+weaponResidentPoolVO.getTotalPulls();
        //3.总金数（每个卡池出金数相加）
        goldenLegendTotalCount = characterActivityPoolVO.getTotalGoldCount()+weaponActivityPoolVO.getTotalGoldCount()
                +characterResidentPoolVO.getTotalGoldCount()+weaponResidentPoolVO.getTotalGoldCount();
        System.out.println("总金数（每个卡池出金数相加） = " + goldenLegendTotalCount);
        //已出金抽卡数
        Double goldenLegendDrawCardCount = getGoldenLegendDrawCardCount(characterActivityPoolVO, weaponActivityPoolVO, characterResidentPoolVO, weaponResidentPoolVO);
        System.out.println("已出金抽卡数 = " + goldenLegendDrawCardCount);
        //4.平均出金数（已出金抽卡数/总金数）（四舍五入，保留一位小数）
        goldenLegendAvgCount = NumberUtil.round(goldenLegendDrawCardCount / goldenLegendTotalCount,1).doubleValue();
        System.out.println("平均出金数（已出金抽卡数/总金数） = " + goldenLegendAvgCount);
        //构建返回的 JSON 字符串，（实体类需要使用 JSONUtil 转换为 JSON 格式）
        return "{" +
                "\"UID\":" + UID + "," +
                "\"drawCardTotalCount\":" + drawCardTotalCount + "," +
                "\"goldenLegendTotalCount\":" + goldenLegendTotalCount + "," +
                "\"goldenLegendAvgCount\":" + goldenLegendAvgCount + "," +
                "\"characterActivityPoolVO\":" + JSONUtil.toJsonStr(characterActivityPoolVO) + "," +
                "\"weaponActivityPoolVO\":" + JSONUtil.toJsonStr(weaponActivityPoolVO) + "," +
                "\"characterResidentPoolVO\":" + JSONUtil.toJsonStr(characterResidentPoolVO) + "," +
                "\"weaponResidentPoolVO\":" + JSONUtil.toJsonStr(weaponResidentPoolVO) +
                "}";
    }

    /**
     * 5.获取所有卡池的已出金抽卡数
     * @param vo1 角色活动池
     * @param vo2 武器活动池
     * @param vo3 角色常驻池
     * @param vo4 武器常驻池
     * @return 所有卡池的已出金抽卡数
     */
    public Double getGoldenLegendDrawCardCount(CharacterActivityPoolVO vo1,WeaponActivityPoolVO vo2,CharacterResidentPoolVO vo3,WeaponResidentPoolVO vo4){
        // 合并所有卡池记录列表
        List<List<Map<String, Object>>> allPools = Arrays.asList(
                vo1.getGoldRecordsList(),
                vo2.getGoldRecordsList(),
                vo3.getGoldRecordsList(),
                vo4.getGoldRecordsList()
        );
        double count = 0.0;
        for (List<Map<String, Object>> pool : allPools) {
            for (Map<String, Object> record : pool) {
                for (Object value : record.values()) {
                    if (value instanceof Number) {
                        count += ((Number) value).doubleValue();
                    }
                }
            }
        }
        return goldenLegendDrawCardCount = count;
    }

    /**
     * 4.获取 武器活动池/角色常驻池/武器常驻池 所需参数方法（通过指定卡池类型获取对应卡池参数）
     * 1.总抽卡数
     * 2.平均出金数（每次出金消耗的抽数之和/总出金数）
     * 3.总出金数
     * 4.出金资源的名称（例如：千古洑流）
     * 5.出金资源对应的抽数（例如：72 抽）
     * 6.已抽但未出金数（总抽卡数-每次出金消耗的抽数之和）
     *
     * @param poolMapList 武器活动池/角色常驻池/武器常驻池抽卡结果列表
     * @param voSupplier 函数接口
     * @param poolName 卡池名称
     */
    public <T extends BasePoolVO> T getPoolParameters(List<Map<String, Object>> poolMapList, Supplier<T> voSupplier, String poolName) {
        // 强制设置标准输出为UTF-8
        System.setOut(new PrintStream(System.out, true, StandardCharsets.UTF_8));

        if (poolMapList.isEmpty()) {
            log.warn("{}还未进行抽取，暂无抽卡记录。", poolName);
            return voSupplier.get();
        }

        // 1. 总抽卡数
        int totalPulls = poolMapList.size();
        // 2. 平均出金数（每次出金消耗的抽数之和/总出金数）
        int avgGoldCount = 0;
        // 3. 总出金数
        int totalGoldCount = 0;
        // 4. 存储每次出金的信息（资源名称和消耗抽数）
        List<Map<String, Object>> goldRecordsList = new ArrayList<>();
        // 5. 已抽但未出金数（总抽卡数-每次出金消耗的抽数之和）
        int notGoldCount = 0;
        // 记录上一次出金的位置（从列表末尾开始计数）
        int lastGoldPositionFromEnd = -1;

        // 从最后一条记录开始向前遍历（从最早到最新）
        for (int i = totalPulls - 1; i >= 0; i--) {
            Map<String, Object> record = poolMapList.get(i);
            int qualityLevel = (int) record.get("qualityLevel");

            if (qualityLevel == 5) { // 出金记录
                totalGoldCount++;

                // 计算当前记录距离列表末尾的位置
                int currentPositionFromEnd = totalPulls - 1 - i;

                // 计算消耗抽数
                int pullCount;
                if (lastGoldPositionFromEnd == -1) {
                    // 第一次出金：从开始到当前的总抽数
                    pullCount = currentPositionFromEnd + 1;
                } else {
                    // 后续出金：与上一次出金的间隔
                    pullCount = currentPositionFromEnd - lastGoldPositionFromEnd;
                }
                lastGoldPositionFromEnd = currentPositionFromEnd;

                // 记录出金信息（资源名称和消耗抽数）
                Map<String, Object> goldRecord = new HashMap<>();
                goldRecord.put("name", record.get("name")); // 资源名称
                goldRecord.put("pullCount", pullCount);     // 消耗抽数
                goldRecordsList.add(goldRecord);
            }
        }

        // 反转结果列表（使最新出金排在最后）
        Collections.reverse(goldRecordsList);

        // 出金消耗的抽数之和
        int totalGoldPullCount = 0;

        // 处理每次出金记录
        for (Map<String, Object> goldRecord : goldRecordsList) {
            String name = (String) goldRecord.get("name");
            int pullCount = (int) goldRecord.get("pullCount");
            totalGoldPullCount += pullCount;
            System.out.println(poolName + "每次出金消耗的抽数：" + pullCount + ", 资源名称: " + name);
        }

        System.out.println(poolName + "总抽卡数: " + totalPulls);
        System.out.println(poolName + "总出金数量: " + totalGoldCount);

        // 平均出金数
        avgGoldCount = totalGoldCount > 0 ? totalGoldPullCount / totalGoldCount : 0;
        System.out.println(poolName + "平均出金数（出金消耗的抽数之和/总出金数）: " + avgGoldCount);

        // 已抽但未出金数
        notGoldCount = totalPulls - totalGoldPullCount;
        System.out.println(poolName + "已抽但未出金数（总抽卡数-出金消耗的抽数之和）: " + notGoldCount);

        // 创建并填充VO
        T resultVO = voSupplier.get();
        resultVO.setBaseParameters(totalPulls, avgGoldCount, totalGoldCount, notGoldCount, goldRecordsList);
        return resultVO;
    }


    /**
     * 3.获取用户不同卡池的抽卡结果列表（共有九种卡池），提取data数据，便于后续对数据进行分析。
     * 抽卡结果列表结构：
     * {
     * "code": 0,
     * "message": "success",
     * "data": [
     * {
     * "cardPoolType": "角色精准调谐",
     * "resourceId": 21050013,
     * "qualityLevel": 3,
     * "resourceType": "武器",
     * "name": "暗夜矩阵·暝光",
     * "count": 1,
     * "time": "2025-08-12 11:27:37"
     * }
     * ]
     * }
     *
     * @param url 《鸣潮》抽卡分析结果页面 URL
     * @return Map<Integer （ 卡池类型 1 - 4 ）, 每个卡池的抽卡结果 ： List < Map < String, Object>> >
     */
    public Map<Integer, List<Map<String, Object>>> getCardDrawingDataToList(String url) {
        //1.获取《鸣潮》抽卡分析结果页面 URL 中的查询参数
        Map<String, String> params = null;
        try {
            params = extractQueryParameters(url);
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
        //2.将查询参数作为请求体，发送请求获取用户的抽卡结果列表（包含9个卡池的抽卡结果）
        // key:cardPoolType,value:抽卡结果列表（该Map按照插入顺序）
        Map<Integer, String> cardDrawingResult = fetchCardDrawingResult(params);
        //3.解析响应数据，并封装到集合中
        Map<Integer, List<Map<String, Object>>> resultMap = new HashMap<>(4);
        for (int key = 1; key <= 4; key++) {
            String jsonStr = cardDrawingResult.get(key);
            if (jsonStr == null) {
                resultMap.put(key, List.of()); // 无数据时放入空列表
                continue;
            }
            // 解析 JSON 字符串
            JSONObject jsonObj = JSONUtil.parseObj(jsonStr);
            // 提取 data 数组
            JSONArray dataArray = jsonObj.getJSONArray("data");
            // 转换每个 data 对象为 Map
            // 使用TypeReference精确指定泛型类型
            List<Map<String, Object>> dataMapList = JSONUtil.toBean(
                    dataArray,
                    new TypeReference<List<Map<String, Object>>>() {
                    },
                    false
            );
            resultMap.put(key, dataMapList);
        }
        return resultMap;
    }

    /**
     * 1.获取《鸣潮》抽卡分析结果页面 URL 中的查询参数
     *
     * @param url 《鸣潮》抽卡分析结果页面请求地址
     * @return URL 中的查询参数集合
     * @throws URISyntaxException
     */
    public Map<String, String> extractQueryParameters(String url) throws URISyntaxException {
        Map<String, String> queryParams = new LinkedHashMap<>();
        // 去除URL前后的空格
        String cleanedUrl = url.trim();
        URI uri = new URI(cleanedUrl);

        // 提取片段（#后面的部分）
        String fragment = uri.getFragment();
        if (fragment == null) {
            //没有查询参数，返回空集合
            return queryParams;
        }
        // 在片段中查找查询参数部分
        int queryStart = fragment.indexOf('?');
        if (queryStart == -1) {
            //没有查询参数，返回空集合
            return queryParams;
        }
        // 获取查询参数字符串（去掉路径部分）
        String queryString = fragment.substring(queryStart + 1);
        // 分割参数
        String[] pairs = queryString.split("&");
        for (String pair : pairs) {
            int idx = pair.indexOf('=');
            if (idx > 0) {
                String key = pair.substring(0, idx);
                String value = pair.substring(idx + 1);
                queryParams.put(key, value);
            }
        }
        return queryParams;
    }


    /**
     * 2.将查询参数作为请求体，发送请求分别获取用户不同卡池的抽卡结果列表（共有九种卡池）
     * TODO：为了加快进度，这里只获取 1~4 卡池的数据，即：角色活动、武器活动、角色常驻、武器常驻 四类
     * 注意：循环获取 cardPoolType=1~4 的卡池数据
     *
     * @param params 查询参数集合
     * @return 包含所有卡池类型结果的 Map（Key: cardPoolType, Value: 响应内容）
     */
    private Map<Integer, String> fetchCardDrawingResult(Map<String, String> params) {
        // 接口地址
        String cardDrawingResultUrl = "https://gmserver-api.aki-game2.com/gacha/record/query";
        // 给UID赋值
        this.UID = params.get("player_id");
        // 创建结果映射（卡池类型 -> 响应内容）（LinkedHashMap：维护插入顺序）
        Map<Integer, String> results = new LinkedHashMap<>(4);
        // 循环处理1-9的卡池类型
        for (int cardPoolType = 1; cardPoolType <= 4; cardPoolType++) {
            // 构建JSON请求体（关键修改：动态设置cardPoolType）
            JSONObject json = new JSONObject();
            json.set("cardPoolId", params.get("gacha_id"))
                    .set("cardPoolType", cardPoolType) // 使用循环变量
                    .set("languageCode", params.get("lang"))
                    .set("playerId", params.get("player_id"))
                    .set("recordId", params.get("record_id"))
                    .set("serverId", params.get("svr_id"));

            try {
                System.out.printf("正在请求卡池类型 %d...%n", cardPoolType);
                // 发送POST请求
                HttpResponse response = HttpRequest.post(cardDrawingResultUrl)
                        .header("Content-Type", "application/json; charset=UTF-8")
                        .body(json.toString())
                        .timeout(30000)
                        .execute();
                // 处理响应
                if (response.isOk()) {
                    String body = response.body();
                    //将抽卡结果存储 Map 中
                    results.put(cardPoolType, body);
                    log.info("卡池类型 {} 请求成功！", cardPoolType);
                } else {
                    String errorMsg = "请求失败，状态码：" + response.getStatus() + "，响应：" + response.body();
                    results.put(cardPoolType, errorMsg);
                    log.error("卡池类型 {} 错误：{}", cardPoolType, errorMsg);
                }
            } catch (Exception e) {
                String exceptionMsg = "请求异常：" + e.getMessage();
                results.put(cardPoolType, exceptionMsg);
                log.error("卡池类型 {} 异常：{}", cardPoolType, exceptionMsg);
                e.printStackTrace();
            }
        }
        //返回所有类型的抽卡结果 Map
        return results;
    }

}
